package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.client.article.IArticleClient;
import com.heima.common.exception.CustomException;
import com.heima.model.common.dtos.*;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.common.pojos.wemedia.WmMaterial;
import com.heima.model.common.pojos.wemedia.WmNews;
import com.heima.model.common.pojos.wemedia.WmNewsMaterial;
import com.heima.model.common.pojos.wemedia.WmUser;
import com.heima.model.common.vo.WmNewsVo;
import com.heima.utils.thread.WmThreadLocalUtil;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.WmAutoScanService;
import com.heima.wemedia.service.WmNewsService;
import com.heima.wemedia.service.WmTaskService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 陈辉
 * @data 2024 10:27
 */
@Service
@Slf4j
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {

    @Autowired
    private WmNewsMaterialMapper wmNewsMaterialMapper;
    @Autowired
    private WmMaterialMapper wmMaterialMapper;
    @Autowired
    private WmAutoScanService wmAutoScanService;
    @Autowired
    private WmTaskService wmTaskService;
    @Autowired
    private WmUserMapper wmUserMapper;

    @Autowired
    private WmNewsMapper wmNewsMapper;

    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;

    /**
     * 内容列表
     *
     * @param reqDto
     * @return
     */
    @Override
    public ResponseResult pageQuery(WmNewsPageReqDto reqDto) {

        //1. 参数校验
        if (reqDto == null)
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        reqDto.checkParam();

        //2. 组装条件wrapper
        LambdaQueryWrapper<WmNews> wrapper = Wrappers.<WmNews>lambdaQuery();
        //2.1 状态
        if (reqDto.getStatus() != null) {
            wrapper.eq(WmNews::getStatus, reqDto.getStatus());
        }
        //2.2 搜索关键字：模糊匹配文章标题
        if (StringUtils.isNotEmpty(reqDto.getKeyword())) {
            wrapper.like(WmNews::getTitle, reqDto.getKeyword());
        }
        //2.3 频道列表
        if (reqDto.getChannelId() != null) {
            wrapper.eq(WmNews::getChannelId, reqDto.getChannelId());
        }
        //2.4 发布日期
        if (reqDto.getBeginPubDate() != null && reqDto.getEndPubDate() != null) {
            wrapper.between(WmNews::getPublishTime, reqDto.getBeginPubDate(), reqDto.getEndPubDate());
        }
        //2.5 按创建时间倒序排列
        wrapper.orderByDesc(WmNews::getCreatedTime);

        //3. 构建分页page
        Page<WmNews> page = new Page<>(reqDto.getPage(), reqDto.getSize());

        //4. 进行分页条件查询
        page(page, wrapper);

        //5. 封装结果返回
        PageResponseResult pr = new PageResponseResult((int) page.getCurrent(), (int) page.getSize(), (int) page.getTotal());
        pr.setData(page.getRecords());
        return pr;
    }

    /**
     * 发布文章
     *
     * @param wmNewsDto
     * @return
     */
    @Override
    @Transactional
    public ResponseResult submit(WmNewsDto wmNewsDto) {
        //0. 参数校验
        if (wmNewsDto == null || StringUtils.isEmpty(wmNewsDto.getTitle())
                || StringUtils.isEmpty(wmNewsDto.getContent()))
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);

        //1. 新增或修改文章信息：wm_news
        //1.1 创建一个实体对象：wmNews
        WmNews news = new WmNews();
        BeanUtils.copyProperties(wmNewsDto, news);
        //1.2 处理文章布局为自动的情况
        if (wmNewsDto.getType() == -1) {
            news.setType(null);
        }
        //1.3 处理封面图片
        if (wmNewsDto.getImages() != null && wmNewsDto.getImages().size() > 0) {
            String images = StringUtils.join(wmNewsDto.getImages(), ",");
            news.setImages(images);
        }
        //1.4 新增或修改文章信息
        saveOrUpdateNews(news);

        //2. 判断本次操作是否为草稿：草稿则结束流程
        if (wmNewsDto.getStatus() == 0)
            return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);

        //文章与素材的关联关系表：wm_news_material
        //3. 提取文章内容中的素材图片，并维护内容图片与素材表的关联关系
        //3.1 解析文章内容，提取出内容图片url
        List<String> imgsInContent = getImgInContent(wmNewsDto.getContent());
        //3.2 记录文章与素材的关联关系：素材为内容引用
        saveRelationWithContentImg(imgsInContent, news);

        //4. 维护封面图片与素材表的关联关系
        saveRelationWithCoverImg(imgsInContent, wmNewsDto, news);

        //开启文章的自动审核流程
        //wmAutoScanService.autoScan(news.getId());

        //异步调用添加任务功能：添加一个审核文章的任务
        wmTaskService.addTask(news);

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 文章上下架
     *
     * @param wmNewsDto
     * @return
     */
    @Override
    public ResponseResult downOrUp(WmNewsDto wmNewsDto) {
        if (wmNewsDto == null || wmNewsDto.getId() == null
                || wmNewsDto.getEnable() == null)
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);

        //1. 根据id查询文章
        WmNews wmNews = getById(wmNewsDto.getId());
        //2. 校验文章是否存在
        if (wmNews == null)
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);

        //3. 校验文章状态是否为已发布：9
        if (wmNews.getStatus() != WmNews.Status.PUBLISHED.getCode())
            throw new CustomException(AppHttpCodeEnum.SERVER_ERROR);

        //4. 修改文章的enable
        wmNews.setEnable(wmNewsDto.getEnable());
        updateById(wmNews);

        //5. 发送消息到kafka，通知article微服务进行app文章的下架
        Map<String, Object> map = new HashMap<>();
        map.put("article_id", wmNews.getArticleId());
        map.put("enable", wmNews.getEnable());   //0:下架， 1：上架
        kafkaTemplate.send("article_down_or_up", JSON.toJSONString(map));
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 自媒体文章分页列表查询
     *
     * @param newsAuthDto
     * @return
     */
    @Override
    public ResponseResult listVo(NewsAuthDto newsAuthDto) {
        //1、参数检验
        if (newsAuthDto == null) {
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }
        newsAuthDto.checkParam();

        //2、分页查询
        Page<WmNews> page = new Page<>(newsAuthDto.getPage(), newsAuthDto.getSize());

        //3、分页查询条件
        LambdaQueryWrapper<WmNews> wrapper = new LambdaQueryWrapper<>();
        //3.1按标题模糊查询
        if (newsAuthDto.getTitle() != null) {
            wrapper.like(WmNews::getTitle, newsAuthDto.getTitle());
        }
        //3.2按审核状态精确检索
        if (newsAuthDto.getStatus() != null && newsAuthDto.getStatus() > 0) {
            wrapper.eq(WmNews::getStatus, newsAuthDto.getStatus());
        }
        //3.3按创建时间倒序查询
        wrapper.orderByDesc(WmNews::getCreatedTime);

        //4、开启查询
        page(page, wrapper);

        //5、封装结果返回
        PageResponseResult result = new PageResponseResult((int) page.getCurrent(), (int) page.getSize(), (int) page.getTotal());
        result.setData(page.getRecords());

        return result;
    }

    /**
     * 文章人工审核通过
     *
     * @param newsAuthDto
     * @return
     */
    @Override
    public ResponseResult authPass(NewsAuthDto newsAuthDto) {
        //1、参数校验
        if (newsAuthDto == null) {
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }
        newsAuthDto.checkParam();

        //2、根据文章id查询文章信息
        WmNews wmNews = wmNewsMapper.selectById(newsAuthDto.getId());
        if (wmNews == null) {
            throw new CustomException(AppHttpCodeEnum.SERVER_ERROR);
        }
        //3、更新自媒体文章状态
        wmNews.setStatus((short) 4);
        wmNews.setUserId(newsAuthDto.getUserId());//用户id
        wmNewsMapper.updateById(wmNews);

        //4、人工审核通过,启动自动远程调用方法将文章信息同步到app端
        wmAutoScanService.saveOrUpdateArticle(wmNews);
       /* wmTaskService.pullTask();*/
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);

    }

    /**
     * 文章人工审核失败
     *
     * @param newsAuthDto
     * @return
     */
    @Override
    public ResponseResult authFail(NewsAuthDto newsAuthDto) {
        //1、参数校验
        if (newsAuthDto == null) {
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }
        newsAuthDto.checkParam();

        //2、根据文章id查询文章信息
        WmNews wmNews = wmNewsMapper.selectById(newsAuthDto.getId());
        if (wmNews == null) {
            throw new CustomException(AppHttpCodeEnum.SERVER_ERROR);
        }
        //3、更改文章状态
        wmNews.setStatus((short) 2);
        wmNews.setReason("文章内容不合规，审核不通过");
        wmNewsMapper.updateById(wmNews);

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 保存封面图片与文章的关联关系
     * 核心职责：
     * 1. 适配封面图片布局自动的情况：
     * a. 当内容图片数量 >= 1 && < 3 , 封面布局适配 单图
     * b. 当内容图片数量 >= 3 , 封面布局适配 三图
     * c. 当内容图片数量 == 0 , 封面布局适配 无图
     * <p>
     * 2. 保存封面图片与文章的关联关系
     *
     * @param imgsInContent 内容图片
     * @param wmNewsDto     包含了封面图片
     * @param news          文章
     */
    private void saveRelationWithCoverImg(List<String> imgsInContent, WmNewsDto wmNewsDto, WmNews news) {
        List<String> images = wmNewsDto.getImages();

        //1. 处理封面为自动的情况
        if (wmNewsDto.getType() == -1) {
            //开启自动适配规则
            if (imgsInContent != null && imgsInContent.size() >= 1 && imgsInContent.size() < 3) {
                //单图: 截取容图片的第一张作为封面图片
                images = imgsInContent.stream().limit(1).collect(Collectors.toList());
                news.setType((short) 1);
            } else if (imgsInContent != null && imgsInContent.size() >= 3) {
                //多图: 截取容图片的前三张作为封面图片
                images = imgsInContent.stream().limit(3).collect(Collectors.toList());
                news.setType((short) 3);
            } else if (imgsInContent == null || images.size() == 0) {
                news.setType((short) 0);
            }

            //更新文章信息
            if (images != null && images.size() > 0) {
                news.setImages(StringUtils.join(images, ","));
            }

            updateById(news);
        }

        //2. 保存封面图片与文章的关联关系
        if (images != null && images.size() > 0) {
            saveRelationWithImg(images, news, 1);
        }
    }

    //保存文章内容图片与文章的关联关系
    private void saveRelationWithContentImg(List<String> imgsInContent, WmNews news) {
        saveRelationWithImg(imgsInContent, news, 0);  //0:内容引用
    }

    /**
     * 保存文章与素材图片的关联关系
     *
     * @param imgs
     * @param news
     * @param type 0：内容引用 1：封面引用
     */
    private void saveRelationWithImg(List<String> imgs, WmNews news, int type) {
        if (imgs != null && imgs.size() > 0) {
            //1. 根据素材url查询素材id    select * from wm_material where url in(????)
            List<WmMaterial> list = wmMaterialMapper.selectList(Wrappers.<WmMaterial>lambdaQuery().in(WmMaterial::getUrl, imgs));
            List<Integer> materialIds = list.stream().map(WmMaterial::getId).distinct().collect(Collectors.toList());

            //2. 保存素材与文章的关联关系
            wmNewsMaterialMapper.saveRelations(materialIds, news.getId(), (short) type);
        }
    }


    //提取文章内容中的图片
    private List<String> getImgInContent(String content) {
        JSONArray jsonArray = JSONArray.parseArray(content);
        List<String> urls = new ArrayList<>();

        for (Object obj : jsonArray) {
            JSONObject jsonObject = JSONObject.parseObject(obj.toString());
            if ("image".equals(jsonObject.getString("type"))) {
                String imgUrl = jsonObject.getString("value");
                urls.add(imgUrl);
            }
        }

        return urls;
    }

    //新增或修改文章信息
    private void saveOrUpdateNews(WmNews news) {
        if (news.getId() == null) {
            //新增
            //设置作者id
            Integer userId = WmThreadLocalUtil.get();
            if (userId == null)
                throw new CustomException(AppHttpCodeEnum.NEED_LOGIN);

            news.setUserId(userId);
            //给定创建时间
            news.setCreatedTime(new Date());
            //默认上架
            news.setEnable((short) 1);
            save(news);
        } else {
            //修改: 先清除文章与原有的素材的关联关系
            //delete from wm_news_material where news_id = ?
            wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery()
                    .eq(WmNewsMaterial::getNewsId, news.getId()));
            //修改: 然后再修改当前文章信息
            news.setSubmitedTime(new Date());
            news.setCreatedTime(new Date());
            updateById(news);
        }
    }


    /**
     * 根据文章id查询文章内容
     *
     * @param id
     * @return
     */
    @Override
    public ResponseResult queryNewsById(Integer id) {
        //1、参数校验
        if (id == null) {
            //抛出参数异常
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }

        //2、调用Mapper查询数据库
        WmNews wmNews = wmNewsMapper.selectById(id);

        //3、判断当前数据是否存在
        if (wmNews == null) {
            //抛出文章不存在异常
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        WmNewsVo wmNewsVo = new WmNewsVo();
        BeanUtils.copyProperties(wmNews, wmNewsVo);
        //4、查询发布文章作者姓名
        WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());
        if (wmUser != null) {
            wmNewsVo.setAuthorName(wmUser.getName());
        }


        //5、将结果返回
        return ResponseResult.okResult(wmNewsVo);
    }


    /**
     * 根据id删除文章内容
     *
     * @param id
     * @return
     */
    @Transactional
    @Override
    public ResponseResult deleteNewsById(Integer id) {
        //1、参数校验
        if (id == null) {
            //抛出参数异常
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }

        //2、调用Mapper查询文章数据库
        WmNews wmNews = wmNewsMapper.selectById(id);

        //3、判断当前数据是否存在
        if (wmNews == null) {
            //抛出文章不存在异常
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);
        }

        //3.2如果是文章已发布 不允许删除
        if (wmNews.getStatus() == 9) {
            //抛出文章已发布不允许删除异常
            throw new CustomException(AppHttpCodeEnum.NEWS_IS_PUBLISHED_NOT_DELETE_ERROR);
        }

        //4、查询文章关联的关联关系表
        List<WmNewsMaterial> wmNewsMaterials = wmNewsMaterialMapper.selectList(Wrappers.<WmNewsMaterial>lambdaQuery()
                .eq(WmNewsMaterial::getNewsId, id));

        //4.2判断关联关系是否存在
        if (wmNewsMaterials == null) {
            //抛出关联关系不存在异常
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);
        }

        //5、存在就删除，先删除关联表 再删除文章表
        wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery()
                .eq(WmNewsMaterial::getNewsId, id));

        //5.2再删除文章表
        wmNewsMapper.deleteById(id);

        //6、将结果返回
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);

    }

}
